﻿# -*- coding: utf-8 -*-

import opcode

class GxLadder(object):
    def __init__(self):
        self._program = None
        self._RUNGs = []
        self._continue = False
        
    def load(self, program):
        L = iter(program)
        self._last = None
        while True:
            rung = self._rung(L, self._last)
            if not rung:
                break
            self._RUNGs.append(rung)
            
    def test(self):
        rung = self._RUNGs[0]
        grid = rung._condition.getLadderGrid()
        painter = TextLadderPainter()
        print painter.draw(grid)
        canvas = TextLadderCanvas()
        painter.drawLadderCell(canvas, grid.getValue(0, 0))
        print canvas.s
        
        
    def _condition(self, L):
        stack = []
        current = None
        self._continue = False
        for cmd in L:
            if cmd.name in opcode.LD:
                if current:
                    stack.append(current)
                current = ConditionBlock()
                current.appendAND(cmd)
            elif cmd.name in opcode.AND:
                if not current:
                    current = ConditionBlock()
                    self._continue = True
                current.appendAND(cmd)
            elif cmd.name in opcode.OR:
                assert current # OR이 처음일 수는 없다.
                current.appendOR(cmd)
            elif cmd.name == "ANB":
                if stack:
                    current = ConditionBlock().AND(stack.pop(), current)
                else:
                    self._continue = True
            elif cmd.name == "ORB":
                current = ConditionBlock().OR(stack.pop(), current)
            else:
                assert not stack # 스택은 비어 있어야 한다.
                L.send(-1)
                self._last = current
                return current
                
    def _output(self, L):
        output = OutputBlock()
        for cmd in L:
            if (cmd.name in opcode.CONDITION or
                cmd.name in opcode.UNCOND_CMDS):
                L.send(-1)
                return output
            else:
                output.append(cmd)
                
    def _branch(self, L):
        output = OutputBlock()
        start = False
        for cmd in L:
            if cmd.name == "MPS":
                start = True
                output.append(self._rung(L))
            elif start and cmd.name in opcode.CONNECT:
                output.append(self._rung(L))
            else:
                L.send(-1)
                return output

    def _rung(self, L, condition=None):
        if not condition:
            condition  = self._condition(L)
        rung = Rung(condition, self._output(L))
        
        self._condition(L)
        if self._continue:
            rung.append(self._rung(L, self._last))
        rung.append(self._branch(L))
        
        return rung
        
    def __str__(self):
        return "\n".join((str(rung) for rung in self._RUNGs))
        
        
        
class LadderPainter:
    pass
    
class LadderCanvas:
    pass
        
        
        
class TextLadderPainter:
    def draw(self, grid):
        L = []
        for row in grid._data:
            L2 = []
            for cell in row:
                L2.append(str(cell))
            L.append("--".join(L2))
        return "\n".join(L)
        
    def drawLadderCell(self, canvas, cell):
        canvas.drawText("| " + str(cell.arguments[0]))
        canvas.drawText("+---| |---")
        
        
        
class TextLadderCanvas():
    def __init__(self):
        self.s = ""
        
    def drawText(self, text):
        self.s += text + "\n"
        
        
        
class LadderCell(object):
    def __init__(self):
        self.instruction = "NOP"
        self.devices = []
        self.wire = []
        
    

class LadderGrid(object):
    def __init__(self, value=None):
        if value:
            self._data = [[value]]
        else:
            self._data = []
        
    def getRows(self):
        return len(self._data)
    
    def getCols(self):
        try:
            return len(self._data[0])
        except IndexError:
            return 0
            
    def getValue(self, row, col):
        return self._data[row][col]
        
    def setValue(self, row, col, value):
        pass
        
    def fillRows(self, count):
        cols = [None]*self.getCols()
        self._data += [cols[:] for i in range(count)]
        
    def fillCols(self, count):
        self._data = [row + [None]*count for row in self._data]

    def appendCol(self, other):
        fill = self.getRows() - other.getRows()
        if fill < 0:
            self.fillRows(-fill)
        elif fill > 0:
            other.fillRows(fill)
        for a, b in zip(self._data, other._data):
            a.extend(b)
        
    def appendRow(self, other):
        fill = self.getCols() - other.getCols()
        if fill < 0:
            self.fillCols(-fill)
        elif fill > 0:
            other.fillCols(fill)
        self._data.extend(other._data)
        
    def __str__(self):
        return ("rows:{0} cols:{1}\n".format(self.getRows(), self.getCols()) +
            "\n".join((str(row) for row in self._data)))

        
def indent(space, lines):
    return "\n".join((space + line for line in lines.split("\n")))


class Rung(object):
    def __init__(self, condition, output):
        self._condition = condition
        self._branch = []
        if output:
            self._branch.append(output)
        
    def append(self, command):
        self._branch.append(command)
        
    def __nonzero__(self):
        if self._condition and self._branch:
            return True
        return False
        
    def __str__(self):
        if self._condition:
            L = [str(self._condition)]
            space = " "*4
        else:
            L = []
            space = ""
        for branch in self._branch:
            if branch:
                L.append(indent(space, str(branch)))
        return "\n".join(L)
        
        
        
class ConditionBlock(object):
    def __init__(self):
        self._ANDs = []
        self._ORs = []
        
    def appendOR(self, condition):
        if self._ANDs:
            if len(self._ANDs) == 1:
                self._ORs.append(self._ANDs.pop())
            else:
                other = ConditionBlock()
                other._ANDs = self._ANDs
                self._ANDs = []
                self._ORs.append(other)
        self._ORs.append(condition)
    
    def appendAND(self, condition):
        if self._ORs:
            other = ConditionBlock()
            other._ORs = self._ORs
            self._ORs = []
            self._ANDs.append(other)
        self._ANDs.append(condition)
        
    def AND(self, x, y):
        self._ANDs.append(x)
        self._ANDs.append(y)
        return self
        
    def OR(self, x, y):
        self._ORs.append(x)
        self._ORs.append(y)
        return self
        
    def __nonzero__(self):
        return len(self._ANDs) > 0 or len(self._ORs) > 0
        
    def _dump(self, L, sep=","):
        s = sep.join((str(i) for i in L))
        if len(L) > 1:
            s = "({0})".format(s)
        return s
        
    def __str__(self):
        if self:
            s = []
            s.append(self._dump(self._ANDs, sep="&"))
            s.append(self._dump(self._ORs, sep="|"))
            return "".join(s)
        else:
            return "(no condition)"
            
    def getLadderGrid(self):
        grid = LadderGrid()
        if self._ANDs:
            for cmd in self._ANDs:
                if isinstance(cmd, ConditionBlock):
                    cmd = cmd.getLadderGrid()
                else:
                    cmd = LadderGrid(cmd)
                grid.appendCol(cmd)
        elif self._ORs:
            for cmd in self._ORs:
                if isinstance(cmd, ConditionBlock):
                    cmd = cmd.getLadderGrid()
                else:
                    cmd = LadderGrid(cmd)
                grid.appendRow(cmd)
        return grid
            
class OutputBlock(object):
    def __init__(self):
        self._CMDs = []

    def append(self, command):
        self._CMDs.append(command)
        
    def __nonzero__(self):
        return len(self._CMDs) > 0
        
    def __str__(self):
        if self._CMDs:
            return "\n".join((str(cmd) for cmd in self._CMDs))
        else:
            return "(no command)"

            
"""
import wx

class LadderCell(object):
    def __init__(self, editor):
        self.editor = editor
        
    def draw(self, dc, width, height):
        dc.Clear()
        rect = wx.Rect(0, 0, width - 2, self.editor.contactHeight)

        g = wx.GraphicsContext.Create(dc)

        g.Translate(0, 1)

        s = "12345678"
        font = g.CreateFont(self.editor.font)
        g.SetFont(font)
        w, h = g.GetTextExtent(s)
        p = (width - w) // 2
        g.DrawText(s, p, 0)
        
        g.Translate(0, self.editor.font.height + 1)
        
        h = self.editor.contactHeight
        p = h // 2
        w = width // 4
        x1 = (width - w) // 2
        x2 = x1 + w - 1
        
        pen = g.CreatePen(wx.BLACK_PEN)
        g.SetPen(pen)
        g.DrawLines(((0, p), (x1, p)))
        g.DrawLines(((x2, p), (width - 1, p)))
        g.DrawLines(((x1, 0), (x1, h - 1)))
        g.DrawLines(((x2, 0), (x2, h - 1)))
        
        g.Translate(0, self.editor.contactHeight + 1)
        
        #g.DrawLines(((0, 0), (width - 1, 0)))


class LadderEditor(wx.Window):
    def __init__(self, parent):
        super(LadderEditor, self).__init__(parent, -1, size=(800, 600))
        
        self.font = wx.Font(9, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL,
            underline=False, faceName=u"돋움체")
        self.SetFont(self.font)
        self.font.width, self.font.height = self.GetTextExtent("8")
        
        # cell item size
        contactHeight = self.font.height + 4
        if self.font.height % 2 == 0:
            contactHeight += 1
        self.contactHeight = contactHeight
        
        self.cell = LadderCell(self)
        
        self.Bind(wx.EVT_PAINT, self.onPaint)
        
    def _drawGrid(self):
        pass
        
    def onPaint(self, event):
        w, h = self.font.width * 9, 100
        if w % 2 == 0:
            w -= 1
        
        dc = wx.PaintDC(self)
        x = 0
        y = 0
        for row in range(7):
            for col in range(10):
                memdc = wx.MemoryDC()
                bitmap = wx.EmptyBitmap(w, h)
                memdc.SelectObject(bitmap)
                memdc.SetFont(self.font)
                self.cell.draw(memdc, w, h)
                dc.Blit(x + w * col, y + h * row, w, h, memdc, 0, 0)
                memdc.SelectObject(wx.NullBitmap)


app = wx.App(redirect=False)
frame = wx.Frame(None, size=(1024, 768))
ladder = LadderEditor(frame)
frame.Show()
app.MainLoop()
"""            
